package pl.pw.mkmw.pnganimator.image.chunks;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.CRC32;
import pl.pw.mkmw.pnganimator.exception.ChunkNotInitializedException;

/**
 *
 * @author Michał
 */
public abstract class ChunkImpl implements Chunk {

    protected int length;
    protected byte[] chunkType;
    protected byte[] chunkData;
    protected byte[] CRC;

    public byte[] getChunk() throws ChunkNotInitializedException {
        if (chunkData == null) {
            throw new ChunkNotInitializedException(this.getClass().getName() + ": nie zainicjowano chunkData\n");
        }

        calculateCRC();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            outputStream.write(ByteBuffer.allocate(4).putInt(this.length).array());
            outputStream.write(this.chunkType);
            outputStream.write(this.chunkData);
            outputStream.write(this.CRC);
        } catch (IOException ex) {
            Logger.getLogger(ChunkImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
        return outputStream.toByteArray();
    }

    @Override
    public byte[] getChunkData() {
        return this.chunkData;
    }

    @Override
    public int getLength() {
        return this.length;
    }

    @Override
    public byte[] getChunkType() {
        return this.chunkType;
    }

    @Override
    public byte[] getCRC() {
        return this.CRC;
    }

    @Override
    public void setChunkData(byte[] data) {
        this.chunkData = data;
        this.length = data.length;
    }

    @Override
    public void setLength(int len) {
        this.length = len;
    }

    @Override
    public void setChunkType(byte[] ct) {
        this.chunkType = ct;
    }

    private void calculateCRC() {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            outputStream.write(this.chunkType);
            outputStream.write(this.chunkData);
        } catch (IOException ex) {
            Logger.getLogger(ChunkImpl.class.getName()).log(Level.SEVERE, null, ex);
        }

        byte c[] = outputStream.toByteArray();
        CRC32 crc = new CRC32();
        crc.update(c);
        this.CRC = new byte[8];
        ByteBuffer bb = ByteBuffer.wrap(this.CRC);
        bb.putLong(crc.getValue());

        if (this.CRC.length > 4 && this.CRC.length <= 8) {
            byte[] tmp = {this.CRC[4], this.CRC[5], this.CRC[6], this.CRC[7]};
            this.CRC = tmp;
        }
    }

    protected abstract void generateChunkData() throws ChunkNotInitializedException;
}
